import java.util.*;

/**
 * Collection Framework Examples
 * This class demonstrates various collection classes and their usage in Java
 */
public class CollectionFramework {
    
    public static void main(String[] args) {
        CollectionFramework example = new CollectionFramework();
        
        // Demonstrating Collection interface and Collections utility class
        example.demonstrateCollectionAndCollections();
        
        // Demonstrating List interface and its implementations
        example.demonstrateListImplementations();
        
        // Demonstrating Set interface and its implementations
        example.demonstrateSetImplementations();
        
        // Demonstrating Map interface and its implementations
        example.demonstrateMapImplementations();
        
        // Demonstrating Iterator and Iterable interfaces
        example.demonstrateIteratorAndIterable();
        
        // Demonstrating Generics usage
        example.demonstrateGenerics();
    }
    
    /**
     * Method to demonstrate Collection interface and Collections utility class
     */
    public void demonstrateCollectionAndCollections() {
        System.out.println("=== Collection Interface and Collections Utility Class ===");
        
        // Creating a collection (using ArrayList as implementation)
        Collection<String> collection = new ArrayList<>();
        
        // Adding elements to collection
        collection.add("Apple");
        collection.add("Banana");
        collection.add("Orange");
        collection.add("Apple"); // Duplicates allowed in Collection (but depends on implementation)
        
        System.out.println("Collection size: " + collection.size());
        System.out.println("Collection elements: " + collection);
        
        // Using Collections utility class methods
        List<String> list = new ArrayList<>(collection);
        System.out.println("Before sorting: " + list);
        
        // Sorting using Collections.sort()
        Collections.sort(list);
        System.out.println("After sorting: " + list);
        
        // Finding max element
        String max = Collections.max(list);
        System.out.println("Max element: " + max);
        
        // Shuffling elements
        Collections.shuffle(list);
        System.out.println("After shuffling: " + list);
        
        System.out.println();
    }
    
    /**
     * Method to demonstrate List interface and its implementations
     */
    public void demonstrateListImplementations() {
        System.out.println("=== List Interface and Implementations ===");
        
        // ArrayList example
        System.out.println("1. ArrayList Example:");
        List<String> arrayList = new ArrayList<>();
        arrayList.add("First");
        arrayList.add("Second");
        arrayList.add("Third");
        System.out.println("ArrayList: " + arrayList);
        System.out.println("Element at index 1: " + arrayList.get(1));
        arrayList.set(1, "New Second");
        System.out.println("After modification: " + arrayList);
        
        // LinkedList example
        System.out.println("\n2. LinkedList Example:");
        List<String> linkedList = new LinkedList<>();
        linkedList.add("First");
        linkedList.add("Second");
        linkedList.add("Third");
        System.out.println("LinkedList: " + linkedList);
        
        // Adding at specific position
        ((LinkedList<String>) linkedList).addFirst("New First");
        ((LinkedList<String>) linkedList).addLast("New Last");
        System.out.println("After adding at first and last: " + linkedList);
        
        // Vector example
        System.out.println("\n3. Vector Example:");
        List<String> vector = new Vector<>();
        vector.add("First");
        vector.add("Second");
        vector.add("Third");
        System.out.println("Vector: " + vector);
        System.out.println("Vector capacity: " + ((Vector<String>) vector).capacity());
        
        System.out.println();
    }
    
    /**
     * Method to demonstrate Set interface and its implementations
     */
    public void demonstrateSetImplementations() {
        System.out.println("=== Set Interface and Implementations ===");
        
        // HashSet example
        System.out.println("1. HashSet Example (Unordered, allows null):");
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Apple");
        hashSet.add("Banana");
        hashSet.add("Orange");
        hashSet.add("Apple"); // Duplicate, will be ignored
        hashSet.add(null); // HashSet allows null
        System.out.println("HashSet: " + hashSet);
        
        // TreeSet example
        System.out.println("\n2. TreeSet Example (Sorted, no null):");
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("Apple");
        treeSet.add("Banana");
        treeSet.add("Orange");
        // treeSet.add(null); // This would throw NullPointerException
        System.out.println("TreeSet: " + treeSet);
        
        // LinkedHashSet example
        System.out.println("\n3. LinkedHashSet Example (Insertion order):");
        Set<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("Apple");
        linkedHashSet.add("Banana");
        linkedHashSet.add("Orange");
        linkedHashSet.add("Apple"); // Duplicate, will be ignored
        System.out.println("LinkedHashSet: " + linkedHashSet);
        
        System.out.println();
    }
    
    /**
     * Method to demonstrate Map interface and its implementations
     */
    public void demonstrateMapImplementations() {
        System.out.println("=== Map Interface and Implementations ===");
        
        // HashMap example
        System.out.println("1. HashMap Example (Unordered, allows null):");
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("Apple", 5);
        hashMap.put("Banana", 3);
        hashMap.put("Orange", 8);
        hashMap.put("Apple", 10); // Duplicate key, updates value
        hashMap.put(null, 1); // HashMap allows one null key
        hashMap.put("Grape", null); // HashMap allows null values
        System.out.println("HashMap: " + hashMap);
        System.out.println("Value for key 'Apple': " + hashMap.get("Apple"));
        
        // TreeMap example
        System.out.println("\n2. TreeMap Example (Sorted by key):");
        Map<String, Integer> treeMap = new TreeMap<>();
        treeMap.put("Apple", 5);
        treeMap.put("Banana", 3);
        treeMap.put("Orange", 8);
        // treeMap.put(null, 1); // This would throw NullPointerException
        System.out.println("TreeMap: " + treeMap);
        
        // LinkedHashMap example
        System.out.println("\n3. LinkedHashMap Example (Insertion order):");
        Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("Apple", 5);
        linkedHashMap.put("Banana", 3);
        linkedHashMap.put("Orange", 8);
        System.out.println("LinkedHashMap: " + linkedHashMap);
        
        // Hashtable example
        System.out.println("\n4. Hashtable Example (Synchronized, no null):");
        Map<String, Integer> hashtable = new Hashtable<>();
        hashtable.put("Apple", 5);
        hashtable.put("Banana", 3);
        hashtable.put("Orange", 8);
        // hashtable.put(null, 1); // This would throw NullPointerException
        // hashtable.put("Grape", null); // This would also throw NullPointerException
        System.out.println("Hashtable: " + hashtable);
        
        System.out.println();
    }
    
    /**
     * Method to demonstrate Iterator and Iterable interfaces
     */
    public void demonstrateIteratorAndIterable() {
        System.out.println("=== Iterator and Iterable Interfaces ===");
        
        // Using Iterator with Collection
        List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E"));
        System.out.println("Original list: " + list);
        
        // Getting iterator
        Iterator<String> iterator = list.iterator();
        
        System.out.println("Traversing with Iterator:");
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.print(element + " ");
        }
        System.out.println();
        
        // Using Iterator to remove elements
        List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
        System.out.println("\nOriginal numbers: " + numbers);
        
        Iterator<Integer> numIterator = numbers.iterator();
        while (numIterator.hasNext()) {
            Integer number = numIterator.next();
            if (number % 2 == 0) { // Remove even numbers
                numIterator.remove();
            }
        }
        System.out.println("After removing even numbers: " + numbers);
        
        // Using for-each loop (Iterable)
        System.out.println("\nTraversing with for-each (Iterable):");
        Set<String> set = new HashSet<>(Arrays.asList("P", "Q", "R", "S"));
        for (String element : set) {
            System.out.print(element + " ");
        }
        System.out.println();
        
        System.out.println();
    }
    
    /**
     * Method to demonstrate Generics usage
     */
    public void demonstrateGenerics() {
        System.out.println("=== Generics Usage ===");
        
        // Without generics - raw types (not recommended)
        List rawList = new ArrayList();
        rawList.add("Hello");
        rawList.add(123); // This is allowed with raw types
        String str = (String) rawList.get(0); // Need explicit casting
        // String str2 = (String) rawList.get(1); // This would cause ClassCastException
        
        // With generics - type safety
        List<String> genericList = new ArrayList<>();
        genericList.add("Hello");
        // genericList.add(123); // Compilation error - type safety
        String genericStr = genericList.get(0); // No need for casting
        System.out.println("Generic list element: " + genericStr);
        
        // Generic method example
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] strArray = {"A", "B", "C", "D", "E"};
        
        System.out.println("Max in int array: " + findMax(intArray));
        System.out.println("Max in string array: " + findMax(strArray));
        
        // Generic class example
        Box<Integer> intBox = new Box<>(100);
        Box<String> strBox = new Box<>("Hello Generics");
        
        System.out.println("Integer box content: " + intBox.getContent());
        System.out.println("String box content: " + strBox.getContent());
        
        System.out.println();
    }
    
    /**
     * Generic method to find maximum element in array
     * @param array the array to find maximum in
     * @return the maximum element
     */
    public <T extends Comparable<T>> T findMax(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i].compareTo(max) > 0) {
                max = array[i];
            }
        }
        return max;
    }
}

/**
 * Generic Box class example
 */
class Box<T> {
    private T content;
    
    public Box(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
    
    public void setContent(T content) {
        this.content = content;
    }
}